import * as path from "path";
import * as fse from "fs-extra";
import * as fs from "fs";
import * as tplCompileUtil from "../util/tpl-compile-util";
import * as npmLoadUtil from "../util/npm-load-util";

let express = require("express");
let router = express.Router();
let request = require("request");
let zlibUtil = require("@qianmi/x-site-common/util/zlib-util");
let tplUtil = require("@qianmi/x-site-common/util/template-util");
let _ = require("@qianmi/x-site-common/util/response-util");
let initService = require("../service/init-service");
let publishLogService = require("../mongoose/service/publish-log-service");

let loadUpdateService = require("../service/load-update-service");
let { ossClient } = loadUpdateService;
let exec = require("child_process").execSync;

//http://dong:3333/tpl/upload/resources
/**
 *
 * //qianmi-web.oss-cn-hangzhou.aliyuncs.com/x-site/developer/resources
 * 部署开发者静态资源, 如图片,JS CSS等文件 到阿里云
 * 路径为
 */
router.post("/upload/resources", function(req, res, next) {
  ossClient
    .putObjectAbsolute(
      new Buffer(req.body.fileContents),
      path.join("/x-site/developer/resources", req.body.uploadPath)
    )
    .then(
      res => {
        console.log("上传开发者静态资源成功!!", res);
        _.resultSucess("OK");
      },
      err => {
        console.log(err);
        _.resultfailure("error");
      }
    );
});

let avaliableRepo = [
  "@qianmi/x-site-ui",
  "@qianmi/x-site-ui-pad",
  "@qianmi/x-site-ui-public-weixin",
  "@qianmi/x-site-ui-zhang",
  "@qianmi/x-site-ui-winter",
  "@qianmi/x-site-ui-marketing",
  "@qianmi/x-site-ui-platform",
  "@qianmi/x-site-ui-wechat",
  "@qianmi/x-site-ui-wang",
  "@qianmi/x-site-ui-weixin",
  "@qianmi/x-site-ui-d2p",
  "@qianmi/x-site-ui-d2p-mobile",
  "@qianmi/x-site-ui-goods-list",
  "@qianmi/x-site-ui-server",
  "@qianmi/x-site-ui-ehome",
  "@qianmi/x-site-ui-bm001",
  "@qianmi/x-site-ui-dong",
  "@qianmi/x-site-ui-rn",
  "@qianmi/x-site-ui-dong",
  "@qianmi/x-site-ui-article",
  "@qianmi/x-site-ui-base-bar",
  "@qianmi/x-site-ui-wxapp",
  "@qianmi/x-site",
  "@qianmi/x-site-ui-goods-desc",
  "@qianmi/ad-screen"
];

/**
 * 获取所有可用的Npm包
 */
router.all("/packages", function(req, res, next) {
  res.status(200).send(_.resultSucess(avaliableRepo));
});

let content =
  "/x-site/${env}/public/template-index/${platForm}-template-index.json";
let tplConfigPath =
  "/x-site/${env}/public/template-index/${platForm}-template-config.json";
let OssPrex = "/x-site/${env}/public/barRepo/";
let OssTplPrex = "/x-site/${env}/public/buildTemplate/";

//http://dong:3333/tpl/reBuild
router.all("/buildAll", function(req, res, next) {
  (async () => {
    await _syncOtherTemplate({
      envCode: req.body.envCode,
      distDir: path.join(__dirname, "../temp")
    });

    res.status(200).send(_.resultSucess("OK"));
  })();
});

//http://dong:3333/tpl/deploy
router.all("/deploy", function(req, res, next) {
  (async () => {
    try {
      let user = req.session.user;
      let username = user && user.username ? user.username : "默默的部署了";
      let name = req.body.name;
      let version = req.body.version;
      let envCode = req.body.envCode;
      let distDir = path.join(__dirname, "../temp");
      let publishTime = getLocalTime(new Date().getTime());

      if (name === "@qianmi/x-site") {
        await initService.initActiveXsite(version);
        //记录部署历史
        await publishLogService.addPublishLog({
          envCode: "test",
          version,
          username,
          publishTime,
          projectName: name,
          operation: "deploy",
          status: true
        });
        res.status(200).send(_.resultSucess("OK"));
      } else if (envCode === "prod" && version.indexOf("RELEASE") < 0) {
        res.send({ result: "warn", message: "线上版本必须部署RELEASE版本" });
      } else if (envCode === "test" || (user && user.username === "admin")) {
        let status = await _dealNpmPackage({ name, version, envCode, distDir });
        //记录部署历史
        await publishLogService.addPublishLog({
          envCode,
          version,
          username,
          status,
          publishTime,
          projectName: name,
          operation: "deploy"
        });

        //同步模板
        if (req.body.syncTemplate) {
          await _syncOtherTemplate({ version, name, envCode, distDir });
        }

        if (status) {
          res.status(200).send(_.resultSucess("OK"));
        } else {
          res.status(500).send(_.resultfailure("error"));
        }
      } else {
        res.send({ result: "login", message: "您没有权限部署该环境" });
      }
    } catch (err) {
      console.log(err);
      res.status(500).send(_.resultfailure("error"));
    }
  })();
});

interface syncOtherTemplateParams {
  envCode: string;
  distDir: string;
  name?: string;
  version?: string;
}
/**
 * 其他模板
 * @param envCode   环境信息
 * @param distDir   临时目录
 * @param name      模板项目名称 可选的 如果不传则同步所有的模板 .
 * @param version   项目版本 可选的
 * @returns {Promise<boolean>}
 * @private
 */
async function _syncOtherTemplate(syncOtherTemplateParams) {
  let { envCode, distDir, name, version } = syncOtherTemplateParams;
  console.log("开始同步其它项目模板。。。");
  let { pcTplConfig, weixinTplConfig, appTplConfig } = await loadTplConfig({
    envCode
  });

  let allTplIndex = pcTplConfig.concat(weixinTplConfig).concat(appTplConfig);
  let _OssTplPrex = OssTplPrex.replace("${env}", envCode);

  for (let i = 0; i < allTplIndex.length; i++) {
    let templateConfig = allTplIndex[i];
    try {
      let str = JSON.stringify(templateConfig);
      // 如果模板中存在部署项目的模板则同步

      let isHit = false;
      //判断是否要同步此模板
      if (name && version) {
        console.log("仅同步引用", name, version, "的模板");
        //遍历用户所传的模板，这些模板不需要同步
        let widgetHead = name.split("/")[1] + "/";
        let userTplPath = path.join(
          distDir,
          "./" + name + "/dist/templateIndex.json"
        );
        let userTplJson = fse.readJsonSync(userTplPath);
        let userTplIndex = {};
        for (let j = 0; j < userTplJson.length; j++) {
          userTplIndex[userTplJson[j].templateName] = j + 1;
        }
        if (
          str.indexOf(widgetHead) >= 0 &&
          !userTplIndex[templateConfig.templateName]
        ) {
          isHit = true;
        }
      } else {
        console.log("同步所有模板");
        isHit = true;
      }

      if (isHit) {
        let {
          templateJs,
          templateCss
        } = await tplCompileUtil.generateTemplateConfigContent({
          projectName: name,
          templateConfig,
          distDir,
          envCode
        });

        if (templateJs) {
          let _content =
            ";//模板同步于 " +
            name +
            " : " +
            version +
            ";\n" +
            templateJs.toString();
          await ossClient.putObjectAbsolute(
            new Buffer(_content, "UTF-8"),
            _OssTplPrex + templateConfig.templateName + ".js"
          );
          await ossClient.putObjectAbsolute(
            new Buffer(templateCss, "UTF-8"),
            _OssTplPrex + templateConfig.templateName + ".css"
          );
        }
      }
    } catch (err) {
      console.error("模板同步失败::", templateConfig.templateName, err.stack);
      throw err;
    }
  }

  //删除本地缓存文件
  let system = process.platform;
  if (system === "win32") {
    exec('rd /s /q "' + path.join(__dirname, "../temp/dist/") + '"');
  } else {
    exec('rm -rf "' + path.join(__dirname, "../temp/dist/") + '"');
  }
  console.log("已完成同步其它项目模板。。。");
  return true;
}

/**
 * 加载指定版本的模板包.
 * @param version
 * @param name
 * @param distDir
 * @param envCode
 * @returns {Promise<boolean>}
 * @private
 */
async function _dealNpmPackage({ version, name, distDir, envCode }) {
  let _OssPrex = OssPrex.replace("${env}", envCode);
  let _OssTplPrex = OssTplPrex.replace("${env}", envCode);

  //下载NPM包. 先验证s本地temp中是否有. 如果有就不加载 了.. 如果没有就去加载..
  //step1 :

  let { npmPackageDir } = await npmLoadUtil.loadNpmPackage({
    version,
    name,
    distDir
  });

  //上传相应的bar文件与模板文件到相应的环境中去..
  console.log("npmPackage   Dir", npmPackageDir);
  let _top = path.join(npmPackageDir, "dist");
  let widgetBarIndex = fse.readJsonSync(path.join(_top, "barIndex.json"));
  //上传barIndex.json到相应的环境中去
  widgetBarIndex.version = version;
  let barIndexPath = _OssPrex + name.slice(8) + "/barIndex.json";
  await ossClient.putObjectAbsolute(
    new Buffer(JSON.stringify(widgetBarIndex)),
    barIndexPath
  );

  let widgetNsArray = [];
  for (let widgetNs in widgetBarIndex) {
    try {
      let path1 = path.join(_top, "barRepo", widgetNs + ".js");
      widgetNsArray.push(widgetNs);
      let isok = await isFileReadAble(path1);
      if (isok) {
        await ossClient.putObjectAbsolute(path1, _OssPrex + widgetNs + ".js");
      }

      let path2 = path.join(_top, "barRepo", widgetNs + "-p.js");
      isok = await isFileReadAble(path2);
      if (isok) {
        await ossClient.putObjectAbsolute(path2, _OssPrex + widgetNs + "-p.js");
      }

      let path3 = path.join(_top, "barRepo", widgetNs + ".css");
      isok = await isFileReadAble(path3);
      if (isok) {
        await ossClient.putObjectAbsolute(path3, _OssPrex + widgetNs + ".css");
      }
    } catch (err) {
      console.log(err);
      return false;
    }
  }

  try {
    await notifyRoachBarCache({ envCode, widgetNsArray });
  } catch (err) {}

  try {
    let tplConfExists = fse.existsSync(path.join(_top, "templateConfig.json"));
    if (!tplConfExists) {
      return true;
    }

    let templateIndex = fse.readJsonSync(path.join(_top, "templateIndex.json"));
    let templateConfig = tplConfExists
      ? fse.readJsonSync(path.join(_top, "templateConfig.json"))
      : [];

    //将数据写入数据库
    let commandPath = path.join(__dirname, "../service/templateinfo2db.js");
    let command = `node --harmony ${commandPath} ${envCode} ${path.join(
      _top,
      "templateConfig.json"
    )}`;
    console.log("模板数据入库==============>\n" + command);
    let c = require("child_process").exec(command);
    c.stdout.on("data", function(s) {
      console.log(s.toString());
    });
    c.stdout.on("end", function() {
      console.log(" done");
    });

    let { pcTplIndex, weixinTplIndex, appTplIndex } = await loadTplIndex({
      envCode
    });
    let { pcTplConfig, weixinTplConfig, appTplConfig } = await loadTplConfig({
      envCode
    });

    //上传模板JS与CSS
    for (let i = 0, ilen = templateIndex.length; i < ilen; i++) {
      let tplInfo = templateIndex[i];
      let tplContent = await getFileContent({
        filePath: path.join(_top, tplInfo.jsFile)
      });
      let _content = ";//版本号：" + version + ";\n" + tplContent.toString();

      await ossClient.putObjectAbsolute(
        new Buffer(_content, "UTF-8"),
        _OssTplPrex + tplInfo.templateName + ".js"
      );
      await ossClient.putObjectAbsolute(
        path.join(_top, tplInfo.cssFile),
        _OssTplPrex + tplInfo.templateName + ".css"
      );
    }

    pcTplIndex = _dealPlatFormIndex({
      userTplIndex: templateIndex,
      platFormTplIndex: pcTplIndex,
      platForm: "pc",
      envCode
    });
    weixinTplIndex = _dealPlatFormIndex({
      userTplIndex: templateIndex,
      platFormTplIndex: weixinTplIndex,
      platForm: "weixin",
      envCode
    });
    appTplIndex = _dealPlatFormIndex({
      userTplIndex: templateIndex,
      platFormTplIndex: appTplIndex,
      platForm: "app",
      envCode
    });
    await uploadTplIndex({ pcTplIndex, weixinTplIndex, appTplIndex, envCode });

    //模板完整信息索引文件
    if (templateConfig.length > 0) {
      pcTplConfig = _dealPlatFormConfig({
        userTplConfig: templateConfig,
        platFormTplConfig: pcTplConfig,
        platForm: "pc",
        envCode
      });
      weixinTplConfig = _dealPlatFormConfig({
        userTplConfig: templateConfig,
        platFormTplConfig: weixinTplConfig,
        platForm: "weixin",
        envCode
      });
      appTplConfig = _dealPlatFormConfig({
        userTplConfig: templateConfig,
        platFormTplConfig: appTplConfig,
        platForm: "app",
        envCode
      });
      await uploadTplConfig({
        pcTplConfig,
        weixinTplConfig,
        appTplConfig,
        envCode
      });
    }

    return true;
  } catch (err) {
    console.log(err);
    return false;
  }
}

async function notifyRoachBarCache({ envCode, widgetNsArray }) {
  let host;
  if (envCode === "test") {
    host = [
      "http://172.21.34.174:3002",
      "http://172.21.34.41:3002",
      "http://172.19.67.186:3002",
      "http://172.19.68.102:3002",
      "http://172.21.33.38:3002",
      "http://172.19.71.144:3002"
    ];
  } else if (envCode === "dong") {
    host = ["http://127.0.0.1:3002"];
  } else if (envCode === "gray") {
    host = ["http://172.17.16.72:3002"];
  } else if (envCode === "prod") {
    host = ["http://172.17.9.65:3002"];
  }

  for (let i = 0, ilen = host.length; i < ilen; i++) {
    console.log("通知小强清理bar缓存:", host[i]);
    let widgetNSArrayGzip = await zlibUtil.gzip(JSON.stringify(widgetNsArray));
    request.post(
      host[i] + "/widget/update",
      { form: { widgetNSArrayGzip } },
      function(err) {
        if (err) {
          console.log("通知小强失败");
        }
      }
    );
  }
}

let platForms = ["pc", "weixin", "app"];
//修改相应环境的模板信息

async function loadTplIndex({ envCode }) {
  try {
    let _content = content.replace("${env}", envCode);

    let response = await ossClient.getObjectAbsolute(
      _content.replace("${platForm}", "pc")
    );
    let pcTplIndex = JSON.parse(response.content.toString());

    response = await ossClient.getObjectAbsolute(
      _content.replace("${platForm}", "weixin")
    );
    let weixinTplIndex = JSON.parse(response.content.toString());

    response = await ossClient.getObjectAbsolute(
      _content.replace("${platForm}", "app")
    );
    let appTplIndex = JSON.parse(response.content.toString());

    return { pcTplIndex, weixinTplIndex, appTplIndex };
  } catch (err) {
    console.log(err);
  }
  return { pcTplIndex: [], weixinTplIndex: [], appTplIndex: [] };
}

async function loadTplConfig({ envCode }) {
  let _path = tplConfigPath.replace("${env}", envCode);
  let pcTplConfig = [];
  let weixinTplConfig = [];
  let appTplConfig = [];
  let response;
  try {
    response = await ossClient.getObjectAbsolute(
      _path.replace("${platForm}", "pc")
    );
    pcTplConfig = JSON.parse(response.content.toString());

    response = await ossClient.getObjectAbsolute(
      _path.replace("${platForm}", "weixin")
    );
    weixinTplConfig = JSON.parse(response.content.toString());

    response = await ossClient.getObjectAbsolute(
      _path.replace("${platForm}", "app")
    );
    appTplConfig = JSON.parse(response.content.toString());
  } catch (err) {
    console.log(err);
  }

  return { pcTplConfig, weixinTplConfig, appTplConfig };
}

/**
 * 上传模板JSON 与JS文件 .. 并在本地建一个备份 ..
 * @param pcTplIndex
 * @param weixinTplIndex
 * @param appTplIndex
 */
async function uploadTplIndex({
  pcTplIndex,
  weixinTplIndex,
  appTplIndex,
  envCode
}) {
  let _content = content.replace("${env}", envCode);

  fse.outputJsonSync(
    path.join(__dirname, "../public/template-index/pc-template-index.json"),
    pcTplIndex
  );
  fse.outputJsonSync(
    path.join(__dirname, "../public/template-index/weixin-template-index.json"),
    weixinTplIndex
  );
  fse.outputJsonSync(
    path.join(__dirname, "../public/template-index/app-template-index.json"),
    appTplIndex
  );

  await ossClient.putObjectAbsolute(
    new Buffer(JSON.stringify(pcTplIndex)),
    _content.replace("${platForm}", "pc")
  );
  await ossClient.putObjectAbsolute(
    new Buffer(JSON.stringify(weixinTplIndex)),
    _content.replace("${platForm}", "weixin")
  );
  await ossClient.putObjectAbsolute(
    new Buffer(JSON.stringify(appTplIndex)),
    _content.replace("${platForm}", "app")
  );

  await ossClient.putObjectAbsolute(
    new Buffer(tplUtil.transfertpIndex2JsFile(pcTplIndex)),
    _content.replace("${platForm}", "pc").replace(".json", ".js")
  );
  await ossClient.putObjectAbsolute(
    new Buffer(tplUtil.transfertpIndex2JsFile(weixinTplIndex)),
    _content.replace("${platForm}", "weixin").replace(".json", ".js")
  );
  await ossClient.putObjectAbsolute(
    new Buffer(tplUtil.transfertpIndex2JsFile(appTplIndex)),
    _content.replace("${platForm}", "app").replace(".json", ".js")
  );
  //上传到阿里云去..
}

//上传模板完整信息索引文件到阿里云
async function uploadTplConfig({
  pcTplConfig,
  weixinTplConfig,
  appTplConfig,
  envCode
}) {
  let _path = tplConfigPath.replace("${env}", envCode);

  await ossClient.putObjectAbsolute(
    new Buffer(JSON.stringify(pcTplConfig)),
    _path.replace("${platForm}", "pc")
  );
  await ossClient.putObjectAbsolute(
    new Buffer(JSON.stringify(weixinTplConfig)),
    _path.replace("${platForm}", "weixin")
  );
  await ossClient.putObjectAbsolute(
    new Buffer(JSON.stringify(appTplConfig)),
    _path.replace("${platForm}", "app")
  );
}

/**
 *
 * @param userTplIndex
 * @param platFormTplIndex
 * @private
 */
function _dealPlatFormIndex({
  userTplIndex,
  platFormTplIndex,
  platForm,
  envCode
}) {
  let templateCode2Index = {};
  for (let i = 0, ilen = userTplIndex.length; i < ilen; i++) {
    templateCode2Index[userTplIndex[i].templateName] = i;
  }

  let toDelTemplateIndex = -1;

  for (let i = 0, ilen = platFormTplIndex.length; i < ilen; i++) {
    if (platFormTplIndex[i].templateName === "first-article") {
      toDelTemplateIndex = i;
      break;
    }
  }
  if (toDelTemplateIndex !== -1) {
    platFormTplIndex.splice(toDelTemplateIndex, 1);
  }

  let platFormTpl2Index = {};
  for (let i = 0, ilen = platFormTplIndex.length; i < ilen; i++) {
    platFormTpl2Index[platFormTplIndex[i].templateName] = i;
  }

  for (let i = 0, ilen = userTplIndex.length; i < ilen; i++) {
    if (userTplIndex[i].platForm === platForm) {
      let jsFile =
          "//qianmi-web.oss-cn-hangzhou.aliyuncs.com/x-site/" +
          envCode +
          "/public/buildTemplate/" +
          userTplIndex[i].templateName +
          ".js",
        cssFile =
          "//qianmi-web.oss-cn-hangzhou.aliyuncs.com/x-site/" +
          envCode +
          "/public/buildTemplate/" +
          userTplIndex[i].templateName +
          ".css";

      if (platFormTpl2Index[userTplIndex[i].templateName]) {
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].detailInfo =
          userTplIndex[i].detailInfo;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].templateName_cn =
          userTplIndex[i].templateName_cn;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].updateTime =
          userTplIndex[i].updateTime;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].isShow =
          userTplIndex[i].isShow;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].platForm =
          userTplIndex[i].platForm;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].pageType =
          userTplIndex[i].pageType;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].pageCode =
          userTplIndex[i].pageCode;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].barMap =
          userTplIndex[i].barMap;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].styleType =
          userTplIndex[i].styleType;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].acceptStyles =
          userTplIndex[i].acceptStyles;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].jsFile = jsFile;
        platFormTplIndex[
          platFormTpl2Index[userTplIndex[i].templateName]
        ].cssFile = cssFile;
      } else {
        platFormTplIndex.push(
          Object.assign({}, userTplIndex[i], { jsFile, cssFile })
        );
      }
    }
  }

  return platFormTplIndex;
}

function _dealPlatFormConfig({
  userTplConfig,
  platFormTplConfig,
  platForm,
  envCode
}) {
  let templateCode2Index = {};
  for (let i = 0, ilen = userTplConfig.length; i < ilen; i++) {
    templateCode2Index[userTplConfig[i].templateName] = i;
  }

  let platFormTpl2Index = {};
  for (let i = 0, ilen = platFormTplConfig.length; i < ilen; i++) {
    platFormTpl2Index[platFormTplConfig[i].templateName] = i;
  }

  for (let i = 0, ilen = userTplConfig.length; i < ilen; i++) {
    if (userTplConfig[i].platForm === platForm) {
      let jsFile =
          "//qianmi-web.oss-cn-hangzhou.aliyuncs.com/x-site/" +
          envCode +
          "/public/buildTemplate/" +
          userTplConfig[i].templateName +
          ".js",
        cssFile =
          "//qianmi-web.oss-cn-hangzhou.aliyuncs.com/x-site/" +
          envCode +
          "/public/buildTemplate/" +
          userTplConfig[i].templateName +
          ".css";
      let tplConfig = Object.assign({}, userTplConfig[i], { jsFile, cssFile });

      if (platFormTpl2Index[userTplConfig[i].templateName]) {
        platFormTplConfig[
          platFormTpl2Index[userTplConfig[i].templateName]
        ] = tplConfig;
      } else {
        platFormTplConfig.push(tplConfig);
      }
    }
  }

  return platFormTplConfig;
}

/**
 * 文件 是否可读 , 如果文件不存在 , 也当做不可读
 * @param filePath
 * @return {Promise}
 */
function isFileReadAble(filePath) {
  return new Promise((resolve, reject) => {
    fs.access(filePath, fs.constants.R_OK, err => {
      if (err) {
        resolve(false);
      } else {
        resolve(true);
      }
      // console.log(err ? 'no access!' : 'can read/write');
    });
  });
}

function getFileContent({ filePath }) {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, (err, data) => {
      if (err) throw err;
      resolve(data);
    });
  });
}

function getLocalTime(timestamp) {
  let date = new Date(timestamp);
  let Y = date.getFullYear() + "-";
  let M =
    (date.getMonth() + 1 < 10
      ? "0" + (date.getMonth() + 1)
      : date.getMonth() + 1) + "-";
  let D = date.getDate() + " ";
  let h = date.getHours() + ":";
  let m = date.getMinutes() + ":";
  let s = date.getSeconds();
  return Y + M + D + h + m + s;
}

module.exports = router;
